#ifndef _UI_WIDGET_H_
#define _UI_WIDGET_H_

#include "../sleipner_config.h"

#include <vector>
#include "math/vec4.h"
#include "ui_sprite.h"
#include "ui_text.h"
#include "ui_mover.h"

#include "input/keyboard.h"
#include "num/named_list.h"

#include "util/delegate.h"

#include <list>

namespace LuaPlus
{
    class LuaObject;
    class LuaState;
};

namespace script
{
	class WriteLua;
};

namespace ui
{
class UISlider;
class UIVerSlider;
class UIProgress;
class UIMultiLine;
class UIDropDown;
class UICheckBox;
class UIFlashWidget;
class UIManager;
class UIMover;
class UIPart2DWidget;
class UIMovieWidget;
struct UISkinElement;
class UIListWidget;
class UIFlexEffect;
class UIBipedWidget;
class UIDragWidget;
class UIGraph;
class UIPolyLine;
class UICalendar;

class UIWidget : public num::NamedList<UIWidget*>
{
public:
	
	enum	WIDGET_IMAGE
	{
		WI_MOUSE_OFF,
		WI_MOUSE_OVER,
		WI_MOUSE_CLICK,
		WI_FULL,
		WI_EMPTY,
		WI_BUTTON,
		WI_BACKGROUND,
		WI_CHECKED,
		WI_UNCHECKED,
		WI_COUNT
	};

	enum	WIDGET_STATE
	{
		WS_MOUSE_OFF,
		WS_MOUSE_OVER,
		WS_MOUSE_CLICK,
		WS_COUNT
	};

	enum	WIDGET_EVENT
	{
		WE_ON_LOAD,
		WE_GAIN_FOCUS,	
		WE_LOOSE_FOCUS,
		WE_DRAG,
		WE_DROP,	
		WE_MOUSE_OVER,
		WE_MOUSE_OFF,
		WE_MOUSE_HOOVER,
		WE_LMB_CLICK,
		WE_LMB_DBL_CLICK,
		WE_LMB_REPEAT,
		WE_RMB_CLICK,
		WE_RMB_DBL_CLICK,
		WE_RMB_REPEAT,
		WE_LMB_RELEASE,
		WE_RMB_RELEASE,
		WE_SLIDER_CHANGE,
		WE_CLIP_ENDED,
		WE_TEXT_ENTER,
		WE_TEXT_NEWINPUT,
		WE_TEXT_SELECTED,
		WE_UP_KEY,
		WE_DOWN_KEY,
		WE_LEFT_KEY,
		WE_RIGHT_KEY,
		WE_TAB_KEY,
		WE_GRAPH_RISEN,
		WE_GRAPH_DROPPED,
		WE_COUNT,
		WE_UNKNOWN
	};
	enum	WIDGET_TYPE
	{
		WT_WIDGET,
		WT_PROGRESS,
		WT_SLIDER,	
		WT_VERSLIDER,
		WT_DROPDOWN,
		WT_MULTILINE,
		WT_CHECKBOX,
		WT_FLASHWIDGET,
		WT_MOVIEWIDGET,
		WT_PARTWIDGET,
		WT_PART2DWIDGET,
		WT_CALENDAR,
		WT_GRAPH,
		WT_POLYLINE,
		WT_FLEXEFFECT,
		WT_LIST,
		WT_BIPED,
		WT_DRAG,
		WT_COUNT
	};

	enum	WIDGET_OVER_MODE
	{
		WO_NONE,
		WO_SCREEN_CENTERED,
		WO_CENTERED,
		WO_TOP_LEFT,
	};

	typedef util::Delegate2<WIDGET_EVENT,UIWidget*>	tdgEvent;
	#define	UI_DELEGATE(CLASS_ID, FUNCTION_POINTER) UTIL_MAKE_DELEGATE_M(ui::UIWidget::tdgEvent, this, CLASS_ID, FUNCTION_POINTER)

						UIWidget(UIWidget* pParent,const std::string& Name = "");
	virtual				~UIWidget();

	/// load from table
	virtual bool		Create(const std::string& Name, const math::Vec2& Pos = math::Vec2(0.5f, 0.5f), const math::Vec2& Size= math::Vec2(0.1f, 0.1f));
	virtual	bool		Load(LuaPlus::LuaObject& _LObj);
	virtual void		PostLoad();
	/// should have an equivalent create ?

	bool				AddChild(UIWidget* pChild);
	bool				InsertChild(UIWidget* pChild, bool bInsertBefore = true, UIWidget* pRef = NULL);
	bool				InsertChild(UIWidget* pChild, bool bInsertBefore, const std::string& Ref);
	bool				RemoveChild(UIWidget* pChild, bool Delete = true);
	bool				RenameWidget(const std::string& NewName);

	virtual bool		ExportToFile(script::WriteLua*	pWL);
	/// update
	virtual	bool		Tick(float dt);

	/// display
	virtual bool		Render(bool Force = false);
	virtual void		PostRender();

	/// Trigger an event artificially
	bool				TriggerEvent(WIDGET_EVENT Event);
	void				FlushSprites();



	/// different events. returns true if consumed
	virtual bool		OnMouseMove(const math::Vec2& From, const math::Vec2& To);
	virtual bool		OnMouseDrag(const math::Vec2& From, const math::Vec2& To);
	virtual bool		OnMouseDrop(const math::Vec2& From, const math::Vec2& To, UIWidget* pDroppedWidget);
	virtual bool		OnMouseHoover(const math::Vec2& Pos);
	virtual bool		OnMousePress(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseDblClick(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseRelease(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseDown(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnMouseRepeat(const math::Vec2& Pos, unsigned int Button);
	virtual bool		OnKeyEvent(const input::Keyboard::KEY& Key);
	virtual void		ResetResources();

	const std::string&	GetName() const { return m_Name; };
	void				SetName(const std::string& Name);
	void				RecalcSize();

	/// retrieve an event string
	bool				CursorInsideWidget(const math::Vec2& Pos);
	bool				CursorInsideActiveWidget(const math::Vec2& Pos);
	virtual bool		IsInside(const math::Vec2& Pos);
	bool				IsValid(WIDGET_STATE state);

	/// accessors
	virtual void		SetPosition(const math::Vec2& Pos);
	const math::Vec2&	GetPosition() const { return m_Pos; };
	virtual void		SetPositionRelativeToParent(const math::Vec2& Pos);
	math::Vec2			GetPositionRelativeToParent() const;

	void				SetSize(const math::Vec2& Size);
	const math::Vec2&	GetSize() const { return m_Size; };

	virtual void		SetColor(float r, float g, float b, float a);
	const COLOR&		GetColor() const { return m_Color; };

	void				SetScale(float Scale);
	float				GetScale() const { return m_Scale; };

	void				SetRotation(float Rot);
	float				GetRotation() const { return m_Rotation; };

	void				SetUV(const math::Vec2& UV1, const math::Vec2& UV2);
	void				GetUV(math::Vec2& UV1, math::Vec2& UV2);

	void				SetCanDrag(bool b) { m_CanDrag = b; };
	bool				GetCanDrag() const { return m_CanDrag; };

	void				SetIsActive(bool b);
	bool				GetIsActive() const 
	{ 
		if (!m_IsActive) return false; 
		if (!m_IsVisible) return false; 
		if (!m_pParent) return true;
		return m_pParent->GetIsActive(); 
	};

	void				SetIsVisible(bool b);
	bool				GetIsVisible() const { return m_IsVisible; };

	void				SetCanEdit(bool b);
	bool				GetCanEdit() const ;

	void				SetCanEditMultiline(bool b);
	bool				GetCanEditMultiline() const;

	void				SetUnderline(bool b) { m_Text.SetUnderline(b); };
	bool				GetUnderline() const { return m_Text.GetUnderline(); };

	ui::UISprite*		GetSprite(WIDGET_STATE State) { return &m_ImageA[State]; };
	ui::UISprite*		GetSprite(WIDGET_IMAGE State) { return &m_ImageA[State]; };

	void				SetSkin(const std::string& Skin);
	std::string			GetSkin();

	// manipulating images
	void				SetImage(WIDGET_STATE State, const std::string& Image);
	void				SetImage(WIDGET_IMAGE State, const std::string& Image);
	void				SetImage(WIDGET_STATE State, const char* ImageData, int DataSize);
	void				SetImage(WIDGET_IMAGE State, const char* ImageData, int DataSize);
	void				SetNextImage(WIDGET_IMAGE State, const std::string& Image) { m_NextImageA[State]=Image; };
	// For setting from script
	void				SetOffImage(const std::string& Image) { SetImage(WS_MOUSE_OFF,Image); };
	void				SetOverImage(const std::string& Image) { SetImage(WS_MOUSE_OVER,Image); };
	void				SetClickImage(const std::string& Image) { SetImage(WS_MOUSE_CLICK,Image); };
	void				SetAllImage(const std::string& Image)
	{
		SetImage(WS_MOUSE_OFF,Image); 
		SetImage(WS_MOUSE_OVER,Image); 
		SetImage(WS_MOUSE_CLICK,Image);
	}
	virtual bool		IsImageValid();

	// manipulating text
	void				SetFont(const std::string& Text);
	const std::string&	GetFont();

	virtual void		SetTextA(const std::string& Text);
	virtual void		SetTextW(const std::wstring& Text);
	virtual void		AppendTextA(const std::string& Text);
	virtual void		AppendTextW(const std::wstring& Text);
	void				TrimSpaces() { m_Text.TrimSpaces(); };
	const std::wstring&	GetText();
	std::string			GetOrigTextA();
	std::string 		GetTextA();
	UIText*				GetTextObj() { return &m_Text; };

	void				ResizeToText();

	const math::Vec2&	GetTextBorder();
	void				SetTextBorder(const math::Vec2& Size);

	void				SetTextColor(float r, float g, float b, float a);
	const COLOR		GetTextColor() const { return m_Text.GetColor(); };

	std::string			GetVAlign();
	void				SetVAlign(const std::string& Align);
	std::string			GetHAlign();
	void				SetHAlign(const std::string& Align);
	void				SelectAllText();

	/// set an event script
	bool				SetEvent(WIDGET_EVENT Event, const std::string& Script);
	/// retrieve the script info
	const std::string&	GetEvent(WIDGET_EVENT Event) const;

	/// set an event delegate
	bool			SetEventDg(WIDGET_EVENT Event, const tdgEvent& adgEvent);
	/// retrieve the event delegate
	const tdgEvent&	GetEventDg(WIDGET_EVENT Event) const;

	// helper functions to access from script
	void				SetGainFocusEvent(const std::string& Script) { SetEvent(WE_GAIN_FOCUS, Script); };
	void				SetGainFocusEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_GAIN_FOCUS, adgEvent); };
	void				SetLooseFocusEvent(const std::string& Script) { SetEvent(WE_LOOSE_FOCUS, Script); };
	void				SetLooseFocusEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_LOOSE_FOCUS, adgEvent); };
	void				SetOnDragEvent(const std::string& Script) { SetEvent(WE_DRAG, Script); };
	void				SetOnDragEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_DRAG, adgEvent); };
	void				SetOnDropEvent(const std::string& Script) { SetEvent(WE_DROP, Script); };
	void				SetOnDropEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_DROP, adgEvent); };
	void				SetMouseOverEvent(const std::string& Script) { SetEvent(WE_MOUSE_OVER, Script); };
	void				SetMouseOverEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_MOUSE_OVER, adgEvent); };
	void				SetMouseOffEvent(const std::string& Script) { SetEvent(WE_MOUSE_OFF, Script); };
	void				SetMouseOffEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_MOUSE_OFF, adgEvent); };
	void				SetMouseHooverEvent(const std::string& Script) { SetEvent(WE_MOUSE_HOOVER, Script); };
	void				SetMouseHooverEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_MOUSE_HOOVER, adgEvent); };
	void				SetClickLMBEvent(const std::string& Script) { SetEvent(WE_LMB_CLICK, Script); };
	void				SetClickLMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_LMB_CLICK, adgEvent); };
	void				SetDblClickLMBEvent(const std::string& Script) { SetEvent(WE_LMB_DBL_CLICK, Script); };
	void				SetDblClickLMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_LMB_DBL_CLICK, adgEvent); };
	void				SetClickRMBEvent(const std::string& Script) { SetEvent(WE_RMB_CLICK, Script); };
	void				SetClickRMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_RMB_CLICK, adgEvent); };
	void				SetDblClickRMBEvent(const std::string& Script) { SetEvent(WE_RMB_DBL_CLICK, Script); };
	void				SetDblClickRMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_RMB_DBL_CLICK, adgEvent); };
	void				SetRepeatLMBEvent(const std::string& Script) { SetEvent(WE_LMB_REPEAT, Script); };
	void				SetRepeatLMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_LMB_REPEAT, adgEvent); };
	void				SetRepeatRMBEvent(const std::string& Script) { SetEvent(WE_RMB_REPEAT, Script); };
	void				SetRepeatRMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_RMB_REPEAT, adgEvent); };
	void				SetReleaseLMBEvent(const std::string& Script) { SetEvent(WE_LMB_RELEASE, Script); };
	void				SetReleaseLMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_LMB_RELEASE, adgEvent); };
	void				SetReleaseRMBEvent(const std::string& Script) { SetEvent(WE_RMB_RELEASE, Script); };
	void				SetReleaseRMBEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_RMB_RELEASE, adgEvent); };
	void				SetClipEndedEvent(const std::string& Script) { SetEvent(WE_CLIP_ENDED, Script); };
	void				SetClipEndedEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_CLIP_ENDED, adgEvent); };
	void				SetTextEnterEvent(const std::string& Script) { SetEvent(WE_TEXT_ENTER, Script); };
	void				SetTextEnterEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_TEXT_ENTER, adgEvent); };
	void				SetTextSelectedEvent(const std::string& Script) { SetEvent(WE_TEXT_SELECTED, Script); };
	void				SetTextSelectedEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_TEXT_SELECTED, adgEvent); };
	void				SetSliderChangeEventDg(const tdgEvent& adgEvent) { SetEventDg(WE_SLIDER_CHANGE, adgEvent); };
	
	/// tested fetches for subsets
	UIWidget*			GetTriggerWidget();	// get the first active widget (used for Dpad navigation)

	UIWidget*			GetParent() const { return m_pParent; };
	UIWidget*			GetPanel();		// recursively get the parent
	UIWidget*			GetWidgetByName(const std::string& Name);
	UIWidget*			GetWidgetByNameEx(const char* Name, ...);
	UISlider*			GetSliderByName(const std::string& Name);
	UIProgress*			GetProgressByName(const std::string& Name);
	UIMultiLine*		GetMultiLineByName(const std::string& Name);
	UIDropDown*			GetDropDownByName(const std::string& Name);
	UICheckBox*			GetCheckBoxByName(const std::string& Name);
#ifndef _PLATFORM_WII
	UIFlashWidget*		GetFlashWidgetByName(const std::string& Name);
#endif
	UIPart2DWidget*		GetPartWidgetByName(const std::string& Name);
	UIMovieWidget*		GetMovieWidgetByName(const std::string& Name);
	UIListWidget*		GetListWidgetByName(const std::string& Name);
	UIFlexEffect*		GetFlexWidgetByName(const std::string& Name);
	UIBipedWidget*		GetBipedWidgetByName(const std::string& Name);
	UIDragWidget*		GetDragWidgetByName(const std::string& Name);
	UIGraph*			GetGraphWidgetByName(const std::string& Name);
	UIPolyLine*			GetPolyLineWidgetByName(const std::string& Name);
	UICalendar*			GetCalendarWidgetByName(const std::string& Name);
	void				GetWidgetsThatHaveTextInName(const std::string& TextInName, std::vector<UIWidget*>& SearchResult); //EMPTY VECTOR MUST BE PASSED FOR PREDICTABLE RESULT!


	void				SetAdditive(bool b);
	bool				IsAdditive();

	bool				ProcessMouseOver();
	bool				ProcessMouseOff();
	virtual void		RefreshText();

	WIDGET_TYPE			GetType();
	std::string			GetTypeName();

	bool				IsActive() const { return GetIsActive(); };
	bool				IsVisible() const { return GetIsVisible(); };
	const std::string&	GetFile() const { return m_ScriptFile; };
	void				SetFile(const std::string& File) { m_ScriptFile = File; };

	void				GetChildren(std::vector<UIWidget*>& Children);
	UIWidget*			GetWidgetAtPos(const math::Vec2& Pos);

///////////////////////////////////////////
// Stuff related to mover animations

	void				DropMover(UIMover* pMover);
	void				AddMover(UIMover* pMover);
	void				ClearMovers();
	void				DeleteMovers();
	bool				HasAnimation();

// accelerators to help add movers to the Widget
	UIMover*			AddUIPanMover(float Delay, float Time, const math::Vec2& To);
	UIMover*			AddUIMoveMover(float Delay, float Time, const math::Vec2& From, const math::Vec2& To);
	UIMover*			AddUISineMover(const math::Vec3& X, const math::Vec3& Y);	
	UIMover*			AddUIXFadeMover(float Delay, float Time, const math::Vec2& AlphaSet);		
	UIMover*			AddProgressMover(float Time, float From, float To);
	UIMover*			AddSetImageMover(float Delay, const std::string& Image);		
	UIMover*			AddUIHideMover();
	UIMover*			AddDelayedActionMover(float Time, UIMover::MoverEvent	Event);
	UIMover*			AddUIPartFadeMover(math::Vec2 TimeSet, math::Vec2 AlphaSet, const std::string& Part);
	UIMover*			AddUIPartZoomMover(math::Vec2 TimeSet, bool ZoomIn, const std::string& Part);
	UIMover*			AddUIPartScaleMover(math::Vec2 TimeSet, bool ZoomIn, const std::string& Part);
	UIMover*			AddUIPartRotateMover(math::Vec2 TimeSet, int RotCount, bool ZoomIn, const std::string& Part);
	UIMover*			AddNumCounterMover(math::Vec2 TimeSet, int From, int To);	
	/////////////////////////////////////////


	// simple helper function to make setting element properties easier
	static void	SetWidgetText(ui::UIWidget* pWidget, const char*  _Msg, ...);

	static WIDGET_TYPE	GetTypeFromString(const std::string& Type);
	static std::string	GetStringFromType(WIDGET_TYPE Type);

	static WIDGET_EVENT	GetEventFromString(const std::string& Name);
	static std::string	GetStringFromEvent(WIDGET_EVENT Event);

	static WIDGET_IMAGE	GetImageStateFromText(const std::string& Text);
	static std::string	GetImageTextFromState(WIDGET_IMAGE wi);

	static	void		Register(LuaPlus::LuaState* _pScript);
	virtual UIWidget*	GetClone();

	virtual void		UpdateLocalization();
	bool				IsInteractive() const;
	void				GetInteractiveWidgets(std::vector<UIWidget*>& List);
	void				FitToRect(const math::Vec2& Rect);

	void				SetOverMode(int aOverMode);
	int					GetOverMode() const;

	bool				BlocksAllEvents() const { return m_BlockAllEvents;		} 
	void				SetBlockAllEvents(bool b) { m_BlockAllEvents = b; };

	void				SetPassword(bool b) { m_Text.SetPassword(b); m_IsPassword = b; };
	bool				GetIsPassword() const { return m_IsPassword; }

	bool				IsParentVisible() const;
	bool				IsMoving() const { return !m_MoverList.empty(); };
	virtual void		RefreshFonts();

	void				SetHooverTip(const std::string& NewTip){m_HooverTip = NewTip;};
	void				SetState(WIDGET_STATE	State) { m_CurrentState = State; };

	void				SetUseMask(bool b);
	bool				UsesMask() const { return m_UseMask; }

	void				SetIsRenderRotated(bool b);
	bool				IsRenderRotated() const { return m_IsRenderRotated; }

	virtual void		OnMaskUpdated() {}
	

protected:
	void				FillCloneInfo(UIWidget* pWidget);
	std::string			GetCloneName(const std::string& Name);
	void				RenderHooverTip();
	/// export widget specifics to file
	virtual bool		ExportSpecificsToFile(script::WriteLua*	pWL);

	// cached copy	
	UIManager*			m_pManager;	
	UIWidget*			m_pParent;
	/// state info
	UISprite			m_ImageA[WI_COUNT];
	std::string			m_NextImageA[WI_COUNT];
	WIDGET_STATE		m_CurrentState;
	UIText				m_Text;
	WIDGET_TYPE			m_Type;

	// sound clip

	// event scripts
	std::string			m_EventA[WE_COUNT];
	tdgEvent			m_EventDg[WE_COUNT];
	
	// name
	std::string			m_Name;
	std::string			m_HooverTip;
	std::string			m_ScriptFile;
	bool				m_IsHoovering;
	bool				m_IsActive;
	bool				m_IsVisible;
	bool				m_CanDrag;
	bool				m_PlaySound;
	bool				m_DisableOverSound, m_DisableClickSound;

//	bool				m_ForceAlpha;		// force alpha for this widget even if it's a top-panel

	// Sprite info
	math::Vec2				m_Pos;			// position of top-left corner
	math::Vec2				m_Size;			// extents of box
	math::Vec2				m_BaseUV1, m_BaseUV2;// UVs for any new image
	float					m_Rotation;
	float					m_Scale;
	COLOR					m_Color;


	// flags for mouse over / mouse off transitions
	// all mouse off needs to be processed before starting mouse over
	bool					m_MouseOverEvent, m_MouseOffEvent;
	bool					m_BlockAllEvents;		// all events not already eated by previous, or children, goes down here

	std::vector<UIMover*>	m_MoverList;

	WIDGET_OVER_MODE		m_OverMode;
	std::string				m_NextWidget;
	std::string				m_PrevWidget;
	bool					m_IsPassword;
	UISkinElement*			m_pSkin;
	bool					m_UseMask;
	bool					m_IsMouseDownHere;
	bool					m_IsRenderRotated;
	
	bool					m_IsImagedLoaded;

};

} // namespace ui

#endif // _UI_WIDGET_H_